﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.IO;

namespace EAPtoHTML
{
    public static class Extensions
    {
        public static void SaveMetaDataAsJSON(this List<DiagramData> l, string ExportPath)
        {
            // get the results
            var data = from item in l
                select new string[]
                    { item.EADiagramGUID
                    , item.EAModelName
                    , item.EADiagramPath
                    , item.EADiagramName
                    , item.EAObjectsInDiagram.ToString()
                    , item.EADiagramKeywords };
            
            //save it in the json format datatables.net likes
            Dictionary<string, object> od = new Dictionary<string, object>();
            od.Add("aaData", data);
            File.WriteAllText(ExportPath,od.toJSON());
                
        }
        public static void SaveDiagramsAsPNG(this List<DiagramData> l,ref EA.Repository r, string ExportPath)
        {

            //get the project interface
            EA.Project prj = r.GetProjectInterface();

            //cycle through all the diagrams and save their png out
            foreach (var id in l.OrderBy(
                x => x.EADiagramPath).ThenBy(
                x => x.EADiagramPath).ThenByDescending(
                x => x.EAObjectsInDiagram).Select(
                x => x.EADiagramGUID))
            {
                //need to open the diagram using the repo. if you don't you will eventually get an error from EA
                int did = ((EA.Diagram)r.GetDiagramByGuid(id)).DiagramID;
                r.OpenDiagram(did);
                prj.PutDiagramImageToFile(id,
                    string.Format("{0}\\data\\{1}.png",
                        ExportPath, Regex.Replace(id, "{|}", string.Empty)), 1);
                r.CloseDiagram(did);
                   
            }

            //cleanup ref to the project interface
            prj = null;
         
        }
    }
    // class to hold the diagram data we actually want.
    //  currently this depends on the 'Repository' which is part of this master class.
    //  it's only needed to walk backwards up the tree to get the model name.
    //  could be better, but no time for it at the moment.
    public class DiagramData
    {
        public string EADiagramGUID { get; set; }
        public string EAModelName { get; set; }
        public string EADiagramPath { get; set; }
        public string EADiagramName { get; set; }
        public int EAObjectsInDiagram { get; set; }
        public string EADiagramKeywords { get; set; }

        public DiagramData(
            string _EADiagramGUID
            , string _EAModelName
            , string _EADiagramPath
            , string _EADiagramName
            , int _EAObjectsInDiagram
            , string _EADiagramKeywords)
        {
            EADiagramGUID =  _EADiagramGUID;
            EAModelName = _EAModelName;
            EADiagramPath = _EADiagramPath;
            EADiagramName = _EADiagramName;
            EAObjectsInDiagram = _EAObjectsInDiagram;
            EADiagramKeywords = _EADiagramKeywords;
        }

        public DiagramData(EA.Repository r, EA.Package p, EA.Diagram d)
        {
            string mn, dp, guid;
            GetModelNameAndPathForPackageByID(r, p, out mn, out dp);
            guid = d.DiagramGUID.Replace(@"{", "").Replace(@"}", "");

            this.EADiagramGUID = d.DiagramGUID;
            this.EAModelName = mn;
            this.EADiagramPath = dp;
            this.EADiagramName = d.Name.ToUpper();
            this.EAObjectsInDiagram = d.DiagramObjects.Count;
            // After getting all the text we want out of the diagram, we are consolidating the words
            //  based on some custom logic that works for us. word consolidator can be omitted
            //  if it is needed. basically words are just compressed to make search in a single
            //  screen a bit more usable as my original export was used on sharepoint and
            //  needed a single page due to using a javascript search. ideally this would be
            //  a real list of all the words and it would be indexed and searchable.
            this.EADiagramKeywords = d.DiagramGUID + " " + d.GetNamesAndNotes(r).ConsolidateKeywords(" ");
        }

        //used to hold the paths to packages, this is for caching paths to trees we have already traversed
        private static Dictionary<int, Tuple<string, string>> p2ps = new Dictionary<int, Tuple<string, string>>();
        //get the model name and path for the package by the package id,
        //  depends on the dictionary above to cache some results so we don't have to traverse the
        //  entire tree in reverse to find out what model we are in. originally i was just passing
        //  the model name through the entire process but decided to do this instead to seperate
        //  this lookup. maybe someone else won't need to know the model. then you could skip this
        //  and simply use the 'full path' i wanted it as a seperate entity though.
        public static void GetModelNameAndPathForPackageByID(EA.Repository r, EA.Package p, out string EAModelName, out string EADiagramPath)
        {

            //remember the original package id so we can cache the value
            int opid = p.PackageID;

            //tuple to hold the model and path in cache
            Tuple<string, string> tv;

            //strings for holding our out values while we work with them
            string mn = string.Empty, dp = string.Empty;

            //if the value is not in the cache, cache it
            if (!p2ps.TryGetValue(opid, out tv))
            {

                //cycle backwards looking for the root (no parents)
                while (p.ParentID != 0)
                {

                    //check to see if our current value is cached
                    if (p2ps.TryGetValue(p.PackageID, out tv))
                    {

                        // if so, we need to prepend the current path with what is cached
                        dp = string.Join(" - ", new string[] { tv.Item2, dp });

                        // and update the model name
                        mn = tv.Item1;

                        // and break out of the loop
                        break;

                    }
                    else
                    {

                        //add to path
                        dp = string.Join(" - ", new string[] { p.Name, dp });

                        // get the parent package
                        p = r.GetPackageByID(p.ParentID);

                        //set the model name
                        mn = p.Name;
                    }

                }

                //fix path (tostring and then remove last three chars)
                dp = dp.Remove(dp.Length - 3);

                //cache our results
                p2ps.Add(
                    opid,
                    new System.Tuple<string, string>(mn.ToUpper(), dp.ToUpper())
                    );
            }

            // get the value
            tv = p2ps[opid];

            //return the results
            EAModelName = tv.Item1;
            EADiagramPath = tv.Item2;

        }

    }
}
